package com.ryan.mdp.advice;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.aopalliance.aop.Advice;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.util.ClassUtils;

import com.ryan.mdp.exception.MdpError;

/**
 * 装载所有的advice.
 * 
 * @author botao.liao
 * 
 */
public class AdviceLoader {
    private static Logger logger = LoggerFactory.getLogger(AdviceLoader.class);
    private static final String REFERENCE_ADVICE_PREFIX = "reference.advice";
    private static final String SERVICE_ADVICE_PREFIX = "service.advice";
    private static final String DEFAULT_ADVICES = "classpath:/mdp_advice_default.properties";
    private static final String CUSTOMER_ADVICES = "classpath:/mdp_advice.properties";
    private static List<Advice> referenceAdvices = null;
    private static List<Advice> serviceAdvices = null;

    public static synchronized List<Advice> loadReferenceAdvices() {
        if (referenceAdvices == null) {
            referenceAdvices = loadAdvices(REFERENCE_ADVICE_PREFIX);
        }
        return referenceAdvices;
    }

    public static synchronized List<Advice> loadServiceAdvices() {
        if (serviceAdvices == null) {
            serviceAdvices = loadAdvices(SERVICE_ADVICE_PREFIX);
        }
        return serviceAdvices;
    }

    public static synchronized void unloadReferenceAdvices() {
        if (referenceAdvices != null) {
            for (Iterator<Advice> iterator = referenceAdvices.iterator(); iterator.hasNext();) {
                Advice advice = iterator.next();
                if (advice instanceof AdviceLifecycle) {
                    ((AdviceLifecycle) advice).destroy();
                }
            }
            referenceAdvices.clear();
        }
    }

    public static synchronized void unloadServiceAdvices() {
        if (serviceAdvices != null) {
            for (Iterator<Advice> iterator = serviceAdvices.iterator(); iterator.hasNext();) {
                Advice advice = iterator.next();
                if (advice instanceof AdviceLifecycle) {
                    ((AdviceLifecycle) advice).destroy();
                }
            }
            serviceAdvices.clear();
        }
    }

    private static List<Advice> loadAdvices(String prefix) {
        List<Advice> advices = new ArrayList<Advice>();
        Properties properties = loadAllAdvices();
        Enumeration<?> propertyNames = properties.propertyNames();
        while (propertyNames.hasMoreElements()) {
            try {
                String key = (String) propertyNames.nextElement();
                String adviceFactory = properties.getProperty(key);
                if (key.startsWith(prefix) && StringUtils.isNotEmpty(adviceFactory)) {
                    int index = Integer.parseInt(key.substring(prefix.length() + 1));
                    Class<?> adviceFactoryClass = ClassUtils.forName(adviceFactory);
                    AdviceFactory factory = (AdviceFactory) adviceFactoryClass.newInstance();
                    Advice advice = factory.getAdvice();
                    if (advice instanceof AdviceLifecycle) {
                        ((AdviceLifecycle) advice).init();
                    }
                    advices.add(index, advice);
                    logger.info("loading advice [{}]", advice.getClass().getName());
                }
            } catch (Exception e) {
                throw new MdpError("01020011", e);
            }
        }
        return advices;
    }

    private static Properties loadAllAdvices() {
        DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
        Resource resource = resourceLoader.getResource(CUSTOMER_ADVICES);
        if (!resource.exists()) {
            resource = resourceLoader.getResource(DEFAULT_ADVICES);
        }
        Properties properties = new Properties();
        try {
            properties.load(resource.getInputStream());
        } catch (IOException e) {
            throw new MdpError("01020004", e);
        }
        return properties;
    }

}
